package com.xomily.web.core.config;

import com.alibaba.fastjson.JSONObject;
import com.xomily.common.utils.ServletUtils;
import com.xomily.common.utils.ip.IpUtils;
import com.xomily.common.utils.uuid.IdUtils;
import com.xomily.devops.domain.DevAlarmLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class RateLimitFilter implements Filter {

    private int rateLimit = 1;

    private long windowSize = 1;

    private ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 将ServletRequest和ServletResponse转换为HttpServletRequest和HttpServletResponse
        // 这样做是为了获得HTTP请求和响应的特定方法
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 获取客户端IP地址
        // 这是为了根据客户端的IP地址来限制请求速率
        String ipAddress = httpRequest.getRemoteAddr();

        // 获取或初始化该IP的速率限制器
        // 这里使用computeIfAbsent方法来避免为每个IP地址重复创建RateLimiter实例
        RateLimiter rateLimiter = rateLimiters.computeIfAbsent(ipAddress, k -> new RateLimiter(rateLimit, windowSize));

        // 检查是否超过了速率限制
        // 如果tryAcquire方法返回false，说明超过了速率限制
        if (!rateLimiter.tryAcquire()) {
            // 设置HTTP响应状态码为429，表示“太多请求”
            // 并向响应体中写入提示信息
            httpResponse.setStatus(429);
            httpResponse.getWriter().write("Rate limit exceeded. Please try again later.");
            // 结束过滤，不再执行后续的请求
            return;
        }

        // 如果没有超过速率限制，则继续执行请求
        // 这里调用chain.doFilter方法来继续执行请求
        chain.doFilter(request, response);
    }

    private class RateLimiter {
        private final int capacity;
        private final long windowSize;
        private final long[] timestamps;

        public RateLimiter(int capacity, long windowSize) {
            this.capacity = capacity;
            this.windowSize = windowSize;
            this.timestamps = new long[capacity];
        }

        /**
         * 尝试获取一个许可
         * 此方法用于在给定的时间窗口内限制请求的频率
         * 它通过检查和更新请求时间戳数组来决定是否允许请求
         *
         * @return 如果成功获取许可，则返回true；否则返回false
         */
        public synchronized boolean tryAcquire() {
            long now = System.currentTimeMillis();

            // 移除过期的请求时间戳
            int i = 0;
            while (i < timestamps.length && timestamps[i] > now - windowSize) {
                i++;
            }
            System.arraycopy(timestamps, i, timestamps, 0, timestamps.length - i);

            // 如果还有空间，添加新的请求时间戳
            if (i < timestamps.length) {
                timestamps[timestamps.length - i - 1] = now;
                return true;
            } else {
                DevAlarmLog devAlarmLog = new DevAlarmLog();
                String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
                devAlarmLog.setIp(ip);
                devAlarmLog.setType("RateLimit");
                devAlarmLog.setCode(IdUtils.fastUUID());
                devAlarmLog.setContent("DDoS攻击 触发，Rate limit exceeded. Please try again later.");
                System.err.println("DDoS攻击 触发，Rate limit exceeded. Please try again later：" + JSONObject.toJSONString(devAlarmLog));
                return false;
            }
        }
    }
}
